// nettest.cc 
//	Test out message delivery between two "Nachos" machines,
//	using the Post Office to coordinate delivery.
//
//	Two caveats:
//	  1. Two copies of Nachos must be running, with machine ID's 0 and 1:
//		./nachos -m 0 -o 1 &
//		./nachos -m 1 -o 0 &
//
//	  2. You need an implementation of condition variables,gmake
//	     which is *not* provided as part of the baseline threads 
//	     implementation.  The Post Office won't work without
//	     a correct implementation of condition variables.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"

#include "system.h"
#include "interrupt.h"
#include "server.h"

void sendMessage(char* data, int PktTo, int MailTo, int MailFrom, int MailType, int lock, int cond){
	PacketHeader outPktHdr;
    MailHeader outMailHdr;

	outPktHdr.to = PktTo;
    outMailHdr.to = MailTo;
    outMailHdr.from = MailFrom;
	outMailHdr.messageType = MailType;
	outMailHdr.lockID = lock;
	outMailHdr.condID = cond;
    outMailHdr.length = strlen(data) + 1;
	
	bool success = postOffice->Send(outPktHdr, outMailHdr, data); 

    if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
}

void receiveMessage(int MailTo){
	PacketHeader inPktHdr;
    MailHeader inMailHdr;
	char buffer[MaxMailSize];
	
	postOffice->Receive(MailTo, &inPktHdr, &inMailHdr, buffer);
    printf("Got \"%s\" in (addr %d box %d) from (addr %d box %d)\n",buffer,inPktHdr.to,inMailHdr.to,inPktHdr.from,inMailHdr.from);
    fflush(stdout);
}

void AcquireLock(int lock){
	char* data = "Acq Lock";
	//acquire lock
	sendMessage(data, SERVER_ID, SERVER_TO, THREAD_TO, LOCK_ACQUIRE, lock, NULL);
	//now we wait till the server has let us acquire lock serverDataLock
	receiveMessage(THREAD_TO);
}

void ReleaseLock(int lock){
	char* data = "Rel Lock";
	//release lock
	sendMessage(data, SERVER_ID, SERVER_TO, THREAD_TO, LOCK_RELEASE, lock, NULL);
	//now we wait till the server has let us acquire lock serverDataLock
	receiveMessage(THREAD_TO);
}

void WaitCond(int lock, int cond){
	char* data = "CV Wait";
	//put ourselves to sleep with CV on lock
	sendMessage(data, SERVER_ID, SERVER_TO, THREAD_TO, COND_WAIT, lock, cond);
	//now we wait till the server will awaken us once someone has signaled
	receiveMessage(THREAD_TO);
}

void SignalCond(int lock, int cond){
	char* data = "CV Signal";
	//signal CV on lock
	sendMessage(data, SERVER_ID, SERVER_TO, THREAD_TO, COND_SIGNAL, lock, cond);
	//now we wait till the server will awaken us once someone has signaled
	receiveMessage(THREAD_TO);
}


//Each SimpleThread called will attempt to acquire the serverDataLock, and then once they have acquired it, release it
void SimpleThread(){
	
	AcquireLock(SERVERDATALOCK);
	ReleaseLock(SERVERDATALOCK);
	
	//we have now released serverDataLock and our thread is now over
}

void InterThread_1(){
	
	AcquireLock(SERVERDATALOCK);
	WaitCond(SERVERDATALOCK,1);
	AcquireLock(SERVERDATALOCK);
	SignalCond(SERVERDATALOCK,1);
	ReleaseLock(SERVERDATALOCK);
	
	//we have now released serverDataLock and our thread is now over
}

void InterThread_2(){
	
	AcquireLock(SERVERDATALOCK);
	SignalCond(SERVERDATALOCK,1);
	WaitCond(SERVERDATALOCK,1);
	ReleaseLock(SERVERDATALOCK);
	
	//we have now released serverDataLock and our thread is now over
}

void ServerManager(){
	PacketHeader inPktHdr;
    MailHeader inMailHdr;
	
	Server* server = new Server();
	
	char buffer[MaxMailSize];
	bool success;
	
	char *response = "Serv resp";
	
	while(true){
		postOffice->Receive(SERVER_TO, &inPktHdr, &inMailHdr, buffer);
		printf("Got \"%s\" in (addr %d box %d) from (addr %d box %d)\n",buffer,inPktHdr.to,inMailHdr.to,inPktHdr.from,inMailHdr.from);
		fflush(stdout);
		
		//only machine ID=0 can send the command to kill the server
		if (inPktHdr.from == KILL_SERVER){
			return;
		}
	
		server->ReceiveMessage(inMailHdr.messageType,inPktHdr.from,inMailHdr.lockID,inMailHdr.condID);
		
		int PktTo = server->GetNextPktDest();
		//if there are packets in the ready queue, continue until all packets have been processed
		while (PktTo != -1){
			sendMessage(response, PktTo, THREAD_TO, SERVER_TO, NULL, NULL, NULL);
			PktTo = server->GetNextPktDest();
		}
	}
}

void KillServer(){
	char *data = "Kill Serv";
	sendMessage(data, SERVER_ID, SERVER_TO, NULL, NULL, NULL, NULL);
}

//create threads
//call a syscall
//have it send a message to the server that keeps track of all the locks, CVS, and MVs

//server machine ID is the same for all
void
MailTest(int threadType)
{

	switch (threadType) {
		default:
			printf("Unknown type - shutting down.\n");
		case KILL_SERVER: //0-thread to kill server
			printf("User prog thread created\n");
			KillServer();
		break;
		case SERVER://1-server
			printf("Server thread created\n");
			ServerManager();
		break;
		case SIMPLETHREAD: //2-simple user prog
			printf("User prog thread created\n");
			SimpleThread();
		break;
		case INTERTHREAD1: //3-intermediate user prog
			printf("User prog thread created\n");
			InterThread_1();
		break;
		case INTERTHREAD2: //4-intermediate user prog
			printf("User prog thread created\n");
			InterThread_2();
		break;
	}
    // Then we're done!
    interrupt->Halt();
}
